package com.blue.common.interceptor;


import com.alibaba.fastjson.JSON;
import com.blue.common.annotation.IgnoreToken;
import com.blue.common.constant.CommonConstant;
import com.blue.common.constant.PathConstant;
import com.blue.common.entity.LoginUserInfo;
import com.blue.common.exception.AuthorException;
import com.blue.common.exception.OfflineException;
import com.blue.common.utils.JwtUtils;
import com.blue.common.utils.RedisUtils;
import com.blue.common.utils.ThreadLocalUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.UUID;

@Component
public class RequestInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisUtils redisUtils;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //放行swagger相关请求
        if ("OPTIONS".equals(request.getMethod())
                || request.getRequestURI().contains("/v3/api-docs")
                || request.getRequestURI().contains("/doc.html")
                || request.getRequestURI().endsWith(".js")
                || request.getRequestURI().endsWith(".css")
                || request.getRequestURI().endsWith(".png")
                || request.getRequestURI().contains("/swagger-resources")) {
            return true;
        }
        //设置请求traceId
        String tid = UUID.randomUUID().toString().replace("-", "");
        MDC.put("traceId", tid);
        //存在IgnoreToken直接放行
        if (handler instanceof HandlerMethod handlerMethod) {
            Method method = handlerMethod.getMethod();
            if (method.isAnnotationPresent(IgnoreToken.class)) {
                LoginUserInfo loginUserInfo = new LoginUserInfo();
                loginUserInfo.setPath(request.getRequestURI());
                loginUserInfo.setParams(getParams(request));
                ThreadLocalUtils.setLoginUserInfo(loginUserInfo);
                return true;
            }
        }
        //校验token
        String token = request.getHeader("token");
        String author = request.getHeader("accessAuthor");
        //判断令牌、解密秘钥
        if (StringUtils.isBlank(token) || StringUtils.isBlank(author) || !"allow".equals(author)) {
            //令牌为空、解密秘钥为空、解密秘钥错误，项客户端返回错误信息
            throw new AuthorException("登录失效，请退出重新登录！");
        }
        //如果令牌存在，则解析令牌
        Claims claims = null;
        try {
            claims = JwtUtils.parseJWT(token);
        } catch (ExpiredJwtException e) {
            throw new AuthorException("登录失效，请退出重新登录！");
        }
        if (claims == null) {
            throw new AuthorException("登录失效，请退出重新登录！");
        }
        //记录请求信息
        String userJson = (String) claims.get("userJson");
        LoginUserInfo loginUserInfo = JSON.parseObject(userJson, LoginUserInfo.class);
        //判断用户是否被离线
        Object value = redisUtils.getValue("offline_key:" + loginUserInfo.getUserId());
        if (CommonConstant.YES.equals(value)) {
            redisUtils.removeValue("offline_key:" + loginUserInfo.getUserId());
            throw new OfflineException("用户已被离线");
        }
        loginUserInfo.setPath(request.getRequestURI());
        loginUserInfo.setParams(getParams(request));
        ThreadLocalUtils.setLoginUserInfo(loginUserInfo);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        MDC.remove("traceId");
        ThreadLocalUtils.clear();
    }

    /**
     * 获取请求参数
     *
     * @param request
     * @return
     * @throws IOException
     */
    private String getParams(HttpServletRequest request) throws IOException {
        String method = request.getMethod();
        if (HttpMethod.GET.name().equals(method)) {
            return request.getQueryString();
            //过滤掉文件上传接口(报错getInputStream() has already been called for this request)
        } else if (HttpMethod.POST.name().equals(method) && !PathConstant.ignorePathList.contains(request.getRequestURI())) {
            RequestWrapper requestWrapper = new RequestWrapper(request);
            return requestWrapper.getBody();
        }
        return null;
    }
}
